home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------------
- #
- #
- # Simple Speech Synthesis Sample Application
- #
- # SpeakToMe
- #
- # SpeakToMe.c - C Source
- #
- # Copyright © 1988, 1994, 1996 Apple Computer, Inc.
- # All rights reserved.
- #
- # Versions: 1.0 5/96
- #
- # Components: SpeakToMe.c May 20, 1996
- # SpeakToMe.r May 20, 1996
- # SpeakToMe.make May 20, 1996
- #
- # The SpeakToMe program demonstrates of how to use the Speech Synthesis Manager
- # in a simple application. In addition to showing how to speak a string or
- # a buffer of text, it exercises the API for selecting a voice and for changing
- # the speaking rate and baseline pitch.
- #
- # The program is still a complete Macintosh application with a Main Event Loop,
- # so there is the extra code to run the MEL.
- #
- # There is a resource file that is necessary as well, to define the Menus, Window,
- # Dialog, and Palette resources used in the program.
- #
- # See Sample and TESample for the general structure and MultiFinder techniques that
- # we recommend that you use when building a new application.
- #
- ------------------------------------------------------------------------------*/
-
- /*
- File SpeakToMe.c
-
- Version 1.0: 5/20/96
- */
-
-
- #include <Types.h>
- #include <Memory.h>
- #include <Resources.h>
- #include <Quickdraw.h>
- #include <Palettes.h>
- #include <Fonts.h>
- #include <Events.h>
- #include <Menus.h>
- #include <Windows.h>
- #include <Dialogs.h>
- #include <Desk.h>
- #include <OSUtils.h>
- #include <ToolUtils.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <strings.h>
- #include <Gestalt.h>
- #include <Speech.h> /* SPEECH SYNTHESIS MANAGER */
-
- /* Constants */
- #define kAppleID 1000 /* resource IDs/menu IDs for Apple, */
- #define kFileID 1001 /* File menu, */
- #define kEditID 1002 /* Edit menu, */
- #define kRateID 1003 /* Speech Rate menu, */
- #define kPitchID 1004 /* Speech Pitch menu, */
- #define kVoiceID 1005 /* and Voice menu */
-
- #define kAppleM 0 /* Index for each menu in array of menu handles */
- #define kFileM 1
- #define kEditM 2
- #define kRateM 3
- #define kPitchM 4
- #define kVoiceM 5
-
- #define kMenuCount 6 /* Total number of menus */
-
- #define kWindowID 1000 /* Resource ID for main window */
- #define kRectID 1000 /* Resource ID for window rect */
- #define kAboutMeDLOG 1000 /* And Resource ID for About box dialog. */
- #define kErrorDLOG 1001 /* And Resource ID for Error dialog. */
-
- #define kSpeakItem 1 /* Start speaking. */
- #define kPauseItem 2 /* Pause speaking. */
- #define kStopItem 3 /* Stop speaking. */
- #define kQuitItem 5 /* Quit in the menu of course. */
-
- #define kClearItem 6 /* Clear in the Edit menu. */
-
- #define kFastItem 1 /* Fast speaking rate. */
- #define kNormalItem 2 /* Normal speaking rate. */
- #define kSlowItem 3 /* Slow speaking rate. */
-
- #define kHighItem 1 /* High pitch range. */
- #define kMidItem 2 /* Middle pitch range. */
- #define kLowItem 3 /* Low pitch range. */
-
- #define kAboutMeCommand 1 /* Menu item in apple menu for About SpeakToMe item */
-
- /* Generic Globals */
- MenuHandle gMenuArray[kMenuCount];
- Rect gDragRect; /* Rectangle used to mark bounds for dragging window */
- Boolean gDoneFlag; /* true if user has chosen Quit command */
- EventRecord gMyEvent;
- WindowPtr gMyWindow,
- gWhichWindow;
- char gEventChar;
- long gDefaultWindowColor = greenColor;
- long gDefaultTextColor = blueColor;
-
- /* Speech Synthesis Globals */
- short gActiveVoiceSelection = 0;
- SpeechChannel gSpeechChan;
- Fixed gDefaultRate;
- Fixed gDefaultPitch;
- Boolean gPauseFlag = false;
- Boolean gRandInitialized = false;
- Str255 gSpeakStr = "";
-
- /* Function Prototypes */
- void SetUpMenus();
- OSErr ShowAboutMeDialog();
- void DoCommand(long int mResult);
- OSErr ChangeVoice (short voiceSelection);
- OSErr InstallVoiceList (MenuHandle mHandle);
- void GetText(char* textString);
- unsigned int RandomizeIndex(unsigned int maxValue);
- void PaintTheWindow(long color);
- void ShowErrorDialog();
-
-
- main()
- {
- long result; /* result of Gestalt call */
- SysEnvRec theWorld; /* result of SysEnvirons call */
- OSErr error; /* error status */
-
- /*
- ** First check to see if the Speech Manager extension is present.
- ** If not, just beep and exit.
- */
- error = Gestalt(gestaltSpeechAttr, &result);
- if ((error) || !(result & (1 << gestaltSpeechMgrPresent))) {
- SysBeep (50);
- ExitToShell(); /* If no Speech Manager, then bail. */
- }
-
- /*
- ** Now test the computer to see if we can do color.
- ** If not we use a black and white window.
- */
- error = SysEnvirons(1, &theWorld);
- if (theWorld.hasColorQD == false) {
- /* If no color QD, we will use black and white. */
- gDefaultWindowColor = whiteColor;
- gDefaultTextColor = blackColor;
- }
-
- InitGraf(&qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- InitDialogs(nil);
- InitCursor();
-
- SetRect(&gDragRect, 4, 24, qd.screenBits.bounds.right - 4, qd.screenBits.bounds.bottom - 4);
- gDoneFlag = false; /* flag to detect when Quit command is chosen */
-
- /*
- ** Open the window.
- */
- gMyWindow = GetNewWindow(kWindowID, nil, (WindowPtr) -1);
- SetPort(gMyWindow);
-
- /*
- ** Set up menus
- */
- SetUpMenus();
-
- /*
- ** Get ready to draw static text
- */
- TextMode(srcCopy);
- TextFont(geneva);
- TextSize(18);
- TextFace(bold);
-
- /*
- ** Main Event Loop
- */
- do {
- SystemTask();
-
- if (WaitNextEvent(everyEvent, &gMyEvent, 5L, NULL)) {
- switch (gMyEvent.what) { /* Case on event type: */
-
- case mouseDown:
- switch (FindWindow(gMyEvent.where, &gWhichWindow)) {
-
- case inSysWindow: /* desk accessory window: call Desk Manager to handle it */
- SystemClick(&gMyEvent, gWhichWindow);
- break;
-
- case inMenuBar: /* Menu bar: learn which command, then execute it. */
- DoCommand(MenuSelect(gMyEvent.where));
- break;
-
- case inDrag: /* title bar: call Window Manager to drag */
- DragWindow(gWhichWindow, gMyEvent.where, &gDragRect);
- break;
-
- case inContent: /* body of application window: */
- if (gWhichWindow != FrontWindow())
- SelectWindow(gWhichWindow); /* make it active if not */
- break;
- }
- break;
-
- case updateEvt: /* refresh the window: */
- if ((WindowPtr) gMyEvent.message == gMyWindow) {
- BeginUpdate((WindowPtr) gMyEvent.message);
-
- PaintTheWindow(gDefaultWindowColor); /* fill in the window color */
-
- MoveTo(16, 28);
- ForeColor(gDefaultTextColor);
- BackColor(gDefaultWindowColor);
- DrawString(gSpeakStr); /* and redraw the text */
-
- EndUpdate((WindowPtr) gMyEvent.message);
- }
- break;
-
- case keyDown:
- case autoKey: /* key pressed once or held down to repeat */
- if (gMyWindow == FrontWindow()) {
- gEventChar = (gMyEvent.message & charCodeMask); /* get the char */
- /*
- ** If Command key down, do it as a Menu Command.
- */
- if (gMyEvent.modifiers & cmdKey)
- DoCommand(MenuKey(gEventChar));
- }
- break;
-
- }
- }
-
- } while (!gDoneFlag);
-
- /* If Speaking then Stop */
- if (SpeechBusy()) StopSpeech(gSpeechChan);
- DisposeSpeechChannel(gSpeechChan);
-
- DisposeWindow (gMyWindow);
- }
-
-
- /*
- ** Read menu descriptions from resource file into memory
- ** and store handles in menu array.
- ** Insert into MenuBar and draw.
- */
- void SetUpMenus()
- {
- int i;
-
- gMenuArray[kAppleM] = GetMenu(kAppleID); /* Read Apple menu from resource file */
- AddResMenu(gMenuArray[kAppleM], 'DRVR'); /* Add desk accessory names to Apple menu */
- gMenuArray[kFileM] = GetMenu(kFileID); /* Read file menu from resource file */
- gMenuArray[kEditM] = GetMenu(kEditID); /* Read edit menu from resource file */
- gMenuArray[kRateM] = GetMenu(kRateID); /* Read rate menu from resource file */
- gMenuArray[kPitchM] = GetMenu(kPitchID); /* Read pitch menu from resource file */
- gMenuArray[kVoiceM] = GetMenu(kVoiceID); /* Read voice menu from resource file */
-
- /* Dynamically insert the available voices into the voice menu */
- InstallVoiceList(gMenuArray[kVoiceM]);
-
- for (i = 0; i < kMenuCount; i++)
- InsertMenu(gMenuArray[i], 0); /* Install menus in menu bar... */
-
- DrawMenuBar(); /* and draw menu bar */
- }
-
-
- /*
- ** Display the dialog box in response to the 'About SpeakIt' menu item
- */
- OSErr ShowAboutMeDialog()
- {
- DialogPtr theDialog;
- short itemHit;
- OSErr error;
-
- /* Before putting up the dialog, start speaking a brief message */
- /* This is the simplest way to produce speech using the synthesis API */
- /* The text in the string will be spoken using the system default voice */
- error = SpeakString("\pHello from the Speech Group!");
- if (!error) { /* If no error then do the dialog */
- theDialog = GetNewDialog(kAboutMeDLOG, nil, (WindowPtr) -1);
- ModalDialog(nil, &itemHit);
- DisposDialog(theDialog);
- }
- return error;
- }
-
-
- /*
- ** Display the dialog box before exiting on error condition
- */
- void ShowErrorDialog()
- {
- DialogPtr theDialog;
- short itemHit;
-
- theDialog = GetNewDialog(kErrorDLOG, nil, (WindowPtr) -1);
- ModalDialog(nil, &itemHit);
- DisposDialog(theDialog);
- }
-
-
- /*
- ** Execute menu command specified by mResult,
- ** the result of MenuSelect
- */
- void DoCommand(long int mResult)
- {
- short theItem, /* menu item number from mResult low-order word */
- theMenu; /* menu number from mResult high-order word */
- Str255 name; /* desk accessory name */
- int temp; /* dummy variable for calling open on desk accessory */
- Fixed newRate, /* new speaking rate */
- newPitch; /* new baseline pitch */
- OSErr error = noErr; /* error status */
-
- theItem = LoWord(mResult); /* Call Toolbox Utility routines to */
- theMenu = HiWord(mResult); /* set menu item number and menu */
-
- switch (theMenu) { /* Switch on menu ID: */
-
- case kAppleID:
- if (theItem == kAboutMeCommand) {
- error = ShowAboutMeDialog();
- }
- else {
- GetItem(gMenuArray[kAppleM], theItem, name);
- temp = OpenDeskAcc(name);
- SetPort(gMyWindow);
- }
- break;
-
- case kFileID:
- if (theItem == kSpeakItem) {
- if (gPauseFlag) { /* If paused then continue speaking */
- error = ContinueSpeech(gSpeechChan);
- /* Reset the pause flag and uncheck the pause item */
- gPauseFlag = false;
- CheckItem(gMenuArray[kFileM], kPauseItem, gPauseFlag);
- }
- else { /* We're not paused, so speak a new phrase: */
- GetText(gSpeakStr); /* Generate a text string */
-
- c2pstr(gSpeakStr); /* Convert to a Pascal string */
- PaintTheWindow(gDefaultWindowColor); /* Paint over the old text... */
- MoveTo(16, 28);
- ForeColor(gDefaultTextColor);
- BackColor(gDefaultWindowColor);
- DrawString(gSpeakStr); /* and draw in the new text */
-
- /* Now go ahead and start speaking */
- error = SpeakText(gSpeechChan, &gSpeakStr[1], gSpeakStr[0]);
- }
- }
- else if (theItem == kPauseItem) {
- /* If already paused then continue speaking */
- if (gPauseFlag) error = ContinueSpeech(gSpeechChan);
- /* Otherwise, pause immediately */
- else error = PauseSpeechAt(gSpeechChan, kImmediate);
- /* Invert the pause flag and check/uncheck the pause item */
- gPauseFlag = !gPauseFlag;
- CheckItem(gMenuArray[kFileM], kPauseItem, gPauseFlag);
- }
- else if (theItem == kStopItem) {
- /* Always try to stop speaking */
- error = StopSpeech(gSpeechChan);
- /* Reset the pause flag and uncheck the pause item */
- gPauseFlag = false;
- CheckItem(gMenuArray[kFileM], kPauseItem, gPauseFlag);
- }
- else if (theItem == kQuitItem)
- gDoneFlag = true;
- break;
-
- case kEditID:
- /* Paint over the text display */
- if (theItem == kClearItem) PaintTheWindow(gDefaultWindowColor);
- break;
-
- case kRateID:
- if (theItem == kFastItem) {
- /* Speed up the speaking rate */
- newRate = gDefaultRate * 1.3; /* Increase default rate by 30% */
- error = SetSpeechRate(gSpeechChan, newRate); /* Set new rate */
- /* Check the fast menu item and uncheck the others */
- CheckItem(gMenuArray[kRateM], kFastItem, true);
- CheckItem(gMenuArray[kRateM], kNormalItem, false);
- CheckItem(gMenuArray[kRateM], kSlowItem, false);
- }
- else if (theItem == kNormalItem) {
- /* Reset the speaking rate */
- error = SetSpeechRate(gSpeechChan, gDefaultRate); /* Set default rate */
- /* Check the normal menu item and uncheck the others */
- CheckItem(gMenuArray[kRateM], kFastItem, false);
- CheckItem(gMenuArray[kRateM], kNormalItem, true);
- CheckItem(gMenuArray[kRateM], kSlowItem, false);
- }
- else if (theItem == kSlowItem){
- /* Slow down the speaking rate */
- newRate = gDefaultRate * 0.7; /* Decrease default rate by 30% */
- error = SetSpeechRate(gSpeechChan, newRate); /* Set new rate */
- /* Check the slow menu item and uncheck the others */
- CheckItem(gMenuArray[kRateM], kFastItem, false);
- CheckItem(gMenuArray[kRateM], kNormalItem, false);
- CheckItem(gMenuArray[kRateM], kSlowItem, true);
- }
- break;
-
- case kPitchID:
- if (theItem == kHighItem) {
- /* Make the baseline pitch higher */
- newPitch = gDefaultPitch + Long2Fix(6L); /* Raise pitch by 1/2 octave */
- error = SetSpeechPitch(gSpeechChan, newPitch); /* Set new pitch */
- /* Check the high menu item and uncheck the others */
- CheckItem(gMenuArray[kPitchM], kHighItem, true);
- CheckItem(gMenuArray[kPitchM], kMidItem, false);
- CheckItem(gMenuArray[kPitchM], kLowItem, false);
- }
- else if (theItem == kMidItem) {
- /* Reset the baseline pitch */
- error = SetSpeechPitch(gSpeechChan, gDefaultPitch); /* Set default pitch */
- /* Check the middle menu item and uncheck the others */
- CheckItem(gMenuArray[kPitchM], kHighItem, false);
- CheckItem(gMenuArray[kPitchM], kMidItem, true);
- CheckItem(gMenuArray[kPitchM], kLowItem, false);
- }
- else if (theItem == kLowItem){
- /* Make the baseline pitch lower */
- newPitch = gDefaultPitch - Long2Fix(6L); /* Lower pitch by 1/2 octave */
- error = SetSpeechPitch(gSpeechChan, newPitch); /* Set new rate */
- /* Check the low menu item and uncheck the others */
- CheckItem(gMenuArray[kPitchM], kHighItem, false);
- CheckItem(gMenuArray[kPitchM], kMidItem, false);
- CheckItem(gMenuArray[kPitchM], kLowItem, true);
- }
- break;
-
- case kVoiceID:
- error = ChangeVoice(theItem);
- break;
- }
-
- if (error) {
- ShowErrorDialog(); /* Display error dialog... */
- gDoneFlag = true; /* ... and terminate the program */
- }
-
- HiliteMenu(0); /* Unhighlight menu title */
- /* (highlighted by MenuSelect) */
- }
-
-
- /*
- ** Respond to Voice Change selection.
- */
- OSErr ChangeVoice(short voiceSelection) {
- VoiceSpec voice; /* voice specification for selected voice */
- OSErr error; /* error status */
-
- if (voiceSelection == gActiveVoiceSelection)
- return noErr; /* Change the voice only if necessary */
-
- /* Get the requested voice spec */
- error = GetIndVoice (voiceSelection, &voice);
-
- /* Get rid of the old speech channel */
- if ((error == noErr) && (gActiveVoiceSelection)) {
- error = DisposeSpeechChannel(gSpeechChan); // Release the channel
- }
-
- /* Allocate a new speech channel */
- if (error == noErr) error = NewSpeechChannel(&voice, &gSpeechChan);
-
- /* Set the default speaking rate and pitch */
- if (error == noErr) error = GetSpeechRate(gSpeechChan, &gDefaultRate);
- if (error == noErr) error = GetSpeechPitch(gSpeechChan, &gDefaultPitch);
-
- /* Check and un-check the correct menu items */
- if (error == noErr) {
- gPauseFlag = false;
- CheckItem(gMenuArray[kFileM], kPauseItem, gPauseFlag);
-
- CheckItem(gMenuArray[kRateM], kFastItem, false);
- CheckItem(gMenuArray[kRateM], kNormalItem, true);
- CheckItem(gMenuArray[kRateM], kSlowItem, false);
-
- CheckItem(gMenuArray[kPitchM], kHighItem, false);
- CheckItem(gMenuArray[kPitchM], kMidItem, true);
- CheckItem(gMenuArray[kPitchM], kLowItem, false);
-
- if (gActiveVoiceSelection != 0) CheckItem (gMenuArray[kVoiceM], gActiveVoiceSelection, false);
- CheckItem (gMenuArray[kVoiceM], voiceSelection, true);
- gActiveVoiceSelection = voiceSelection; /* Assign the global voice selection */
- }
-
- return error;
- }
-
-
- /*
- ** Create Menu entries for each available voice.
- ** Select System Default voice.
- */
- OSErr InstallVoiceList(MenuHandle mHandle) {
- short voiceCount, i;
- VoiceSpec voice;
- VoiceDescription desc, defaultDesc;
- OSErr error;
-
- /* Get the description for the system default voice */
- defaultDesc.length = sizeof(VoiceDescription);
- error = GetVoiceDescription(NULL, &defaultDesc, defaultDesc.length);
-
- /* Count the number of available voices (for all synthesizers) */
- error = CountVoices (&voiceCount);
- if (error != noErr) return error;
-
- if (voiceCount > 0) {
- /* Install each voice in the Voice menu and make the system default the active voice */
- for (i = 1; i <= voiceCount; ++i) {
- // get the next indexed voice
- error = GetIndVoice (i, &voice);
- if (error == noErr) {
- desc.length = sizeof (VoiceDescription);
- error = GetVoiceDescription (&voice, &desc, desc.length);
- if (error == noErr) {
- /* Install the menu first with blank text (can't be empty) so that we */
- /* don't have any menu metacharacters (like parentheses or slash, etc.) */
- /* that may be in the voice name and get interpreted by the Menu Manager */
- AppendMenu(mHandle, "\p ");
- /* Get the item number of the newly added menu */
- /* create the menu item to be inserted using the voice name */
- SetItem (mHandle, i, desc.name);
- if ((desc.voice.id == defaultDesc.voice.id) /* Compare voice being added to default voice */
- && (desc.language == defaultDesc.language)) /* voice IDs are only unique within a given language */
- ChangeVoice(i); /* Make the system default the active selection */
- }
- }
- } /* end of for loop through voices */
- } /* end of VoiceCount check */
-
- return error;
- }
-
-
- /*
- ** Create string to speak from predefined arrays.
- ** String will be a random variation on the old adage:
- ** "A bird in the hand is worth 2 in the bush."
- */
- void GetText(char* textToSpeak) {
- /* Set up string arrays */
- const char *nounChoices[] = {
- "bird",
- "fish",
- "snake",
- "weasel",
- "cat",
- "pig",
- "worm",
- "fly",
- "fox",
- "monkey",
- 0
- };
-
- const char *containerChoices[] = {
- "hand",
- "pan",
- "basket",
- "barn",
- "bag",
- "sty",
- "hole",
- "jar",
- "coop",
- "zoo",
- 0
- };
-
- const char *numberChoices[] = {
- "none",
- "2",
- "3",
- "4",
- "5",
- "6",
- "7",
- "8",
- "9",
- "10",
- 0
- };
-
- const char *locationChoices[] = {
- "bush",
- "water",
- "grass",
- "den",
- "tree",
- "mud",
- "compost",
- "ointment",
- "woods",
- "jungle",
- 0
- };
- /* Generate string by making random choices from arrays */
- textToSpeak[0] = '\0';
- strcpy(textToSpeak, "A ");
- strcat(textToSpeak, nounChoices[RandomizeIndex(9)]);
- strcat(textToSpeak, " in the ");
- strcat(textToSpeak, containerChoices[RandomizeIndex(9)]);
- strcat(textToSpeak, " is worth ");
- strcat(textToSpeak, numberChoices[RandomizeIndex(9)]);
- strcat(textToSpeak, " in the ");
- strcat(textToSpeak, locationChoices[RandomizeIndex(9)]);
- strcat(textToSpeak, ".");
- }
-
-
- /*
- ** Return a random index between 0 and the maxValue parameter.
- ** Uses the standard C "rand" function with the current time as a seed value.
- */
- unsigned int RandomizeIndex(unsigned int maxValue) {
- unsigned long seedVal;
- unsigned int result,randFactor;
-
- /* Divide up the range of potential values based on the desired maximum value */
- randFactor = RAND_MAX / maxValue;
- /* Initialize the random sequence with a seed value derived from the current time */
- if (!gRandInitialized) {
- ReadDateTime(&seedVal);
- srand((unsigned int)seedVal);
- gRandInitialized = true;
- }
- result = rand()/randFactor;
-
- return result;
- }
-
-
- /*
- ** Simple function for filling in the window color
- */
- void PaintTheWindow(long color)
- {
- Handle resource;
-
- resource = GetResource('RECT', kRectID);
- if ( resource != nil ) {
- ForeColor(color);
- PaintRect(*(Rect**) resource);
- }
- }
-
-